/*
 * @Author: wuqingchun
 * @Date: 2024-07-02 20:20:41
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-07-08 16:38:37
 */

#include <thread>
#include <chrono>

#include "arq_timer.h"

CARQTimer::CARQTimer()
{
    m_uiNextTaskId = 0;
}

uint32_t CARQTimer::AddTask(uint32_t uiInitUS, uint32_t uiIntervalUS, std::function<void(const void*)> fnCallBack, const void* pPrivate)
{
    TIMER_TASK_S* pstTask = new (std::nothrow) TIMER_TASK_S();
    if (pstTask == nullptr)
    {
        return 0;
    }

    pstTask->fnCallBack = fnCallBack;
    pstTask->ui64Interval = uiIntervalUS;
    pstTask->ui64Interval *= 1000;
    pstTask->pPrivate = pPrivate;
    pstTask->ui64Timeout = uiInitUS;
    pstTask->ui64Timeout *= 1000;

    auto now = std::chrono::steady_clock::now().time_since_epoch().count();
    pstTask->ui64Timeout += now;
    std::unique_lock<std::mutex> objUL(m_objMtx);
    pstTask->uiTaskID = ++m_uiNextTaskId;
    m_mapTasks[pstTask->ui64Timeout] = pstTask;

    return pstTask->uiTaskID;
}

void CARQTimer::RemoveTask(std::function<bool (const void*, const void*)> fnCmp, const void* pKey)
{
    std::unique_lock<std::mutex> objUL(m_objMtx);

    for (auto iter = m_mapTasks.begin(); iter != m_mapTasks.end();)
    {
        auto tmpiter = iter;
        iter++;
        if (fnCmp(tmpiter->second->pPrivate, pKey))
        {
            delete tmpiter->second;
            m_mapTasks.erase(tmpiter);
        }
    }
}

void CARQTimer::Run()
{
    TIMER_TASK_S* pstTask = nullptr;

    {
        std::unique_lock<std::mutex> objUL(m_objMtx);
        auto iter = m_mapTasks.begin();
        if (iter != m_mapTasks.end())
        {
            pstTask = iter->second;
            m_mapTasks.erase(iter);
        }
    }

    if (pstTask == nullptr)
    {
        return;
    }
    
    auto now = std::chrono::steady_clock::now().time_since_epoch().count();
    if (now < pstTask->ui64Timeout)
    {
        std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::time_point() + 
                                                          std::chrono::nanoseconds(pstTask->ui64Timeout);
        std::this_thread::sleep_until(timePoint);
    }

    pstTask->fnCallBack(pstTask->pPrivate);

    if (pstTask->ui64Interval > 0)
    {
        std::unique_lock<std::mutex> objUL(m_objMtx);
        pstTask->ui64Timeout = std::chrono::steady_clock::now().time_since_epoch().count() +  pstTask->ui64Interval;
        m_mapTasks[pstTask->ui64Timeout] = pstTask;
    }
    else
    {
        delete pstTask;
    }
}